David Stonestrom 6/4/2014
Writing a tutorial that demonstrates dealing with solver errors is tricky because the people who write/maintain the solvers are constantly updating the solvers to deal with known issues. There is a solid chance that by the time you are reading this the code below will no longer generate errors. However, the notes which accompany the code are still the correct procedure for handling solver errors. As of the time of this writing, the exact problem below resulted in three different outcomes depending on which solver was used. The problem is:
where $A$ is one row and has one negative entry and $p \succeq 0$. The negative entry in $A$ makes the problem unbounded, so the correct solution is:
objective value: inf
status: unbounded
In [1]:
import cvxpy as cvx
import numpy
n = 6;
m = 1;
# generate some constraints
numpy.random.seed(0) # for repeatibility
A = numpy.random.randn(m,n)
b = 10 * numpy.random.random([m,1])
p = cvx.Parameter(1,n,nonneg=True)
p.value = numpy.random.random([1,n]) # returnes values in the interval [0.0, 1.0)
x = cvx.Variable(n,1) # (n,1) will give a colum vector
objective = cvx.Maximize(p * cvx.sqrt(x))
constraint = [A*x <= b]
problem = cvx.Problem(objective, constraint)
print "A: ", A
print "b: ", b
print "p: ", p.value
In [2]:
try:
print problem.solve(solver = cvx.ECOS) # the default
print problem.status
except Exception as e:
print e
This is the classic solver error outcome. The objective is 'None' and the status is 'solver_error.' Note that it did not raise an exception.
When this happens, the next step is to just try the other available solvers to see what they do.
In [3]:
try:
print problem.solve(solver=cvx.CVXOPT)
print problem.status
except Exception as e:
print e
Using CVXOPT throws an exception: 'math domain error.' This will probably be rolled into the solver_error case above at some point.
SCS is the only solver left to try.
In [4]:
try:
print problem.solve(solver=cvx.SCS)
print problem.status
except Exception as e:
print e
Here the numerical imprecision of SCS lets it find an optimal value. Note the printed objective value: 2082. Compare it to the numpy computed objective for the optimal value of x below:
In [5]:
print "numpy calculated objective value:", p.value.dot(numpy.sqrt(x.value))
The two values differ by over 10%. Next is a demonstration of a better feasible point.
In [6]:
print "SCS objective value: ", p.value.dot(numpy.sqrt(x.value))
print "SCS constraint violation: ", A.dot(x.value) - b
x_better = x.value + [[0],[0],[9900000],[0],[0],[10000000]]
print "\nHigher objective and lower constraint violation:"
print "objective: ", p.value.dot(numpy.sqrt(x_better))
print "constraint violation: ", A.dot(x_better) - b
Compare the above results to the following unbounded problem which ECOS solves corectly.
$$ \textbf{maximize } f\left(x\right) = \sum_{i=1}^{6}\sqrt{x_i}$$
In [7]:
x = cvx.Variable(6)
problem = cvx.Problem(cvx.Maximize(cvx.sum(cvx.sqrt(x))))
try:
print "ECOS:\n", problem.solve(solver=cvx.ECOS)
print problem.status
print "\nSCS:\n", problem.solve(solver=cvx.SCS)
print problem.status
print "\nCVXOPT:\n", problem.solve(solver=cvx.CVXOPT)
print problem.status
except Exception as e:
print "Error Error Error"
print e
The takeaway from all of this is to try all the solvers and make sure that you believe the answer they give. One of the important pieces of this is manually testing the constraint violations and objective value. Also consider what the value of the objective and variables are saying about the origional problem.
Finally, please keep in mind that this is an open source project and is provided "as is" with no guarintee of correctness.